<script setup lang="ts">
import { ref, watch } from 'vue'

type ImageInfo = { width: number; height: number; sizeKB: number }

// 图片 -> Base64
const fileStatus = ref<string>('等待上传图片')
const fileBase64 = ref<string>('')
const fileInfo = ref<ImageInfo | null>(null)
const filePreview = ref<string>('')

function formatKB(bytes: number) {
  return (bytes / 1024).toFixed(2)
}

function onFileChange(e: Event) {
  const input = e.target as HTMLInputElement
  const file = input.files?.[0]
  if (!file) {
    fileStatus.value = '未选择文件'
    fileBase64.value = ''
    fileInfo.value = null
    filePreview.value = ''
    return
  }
  if (!file.type.startsWith('image/')) {
    fileStatus.value = '请选择图片文件'
    return
  }
  fileStatus.value = '读取中...'
  const reader = new FileReader()
  reader.onload = () => {
    const dataUrl = String(reader.result)
    fileBase64.value = dataUrl
    filePreview.value = dataUrl
    const img = new Image()
    img.onload = () => {
      fileInfo.value = {
        width: img.width,
        height: img.height,
        sizeKB: Number(formatKB(file.size)),
      }
      fileStatus.value = '转换完成'
    }
    img.onerror = () => {
      fileStatus.value = '图片加载失败'
    }
    img.src = dataUrl
  }
  reader.onerror = () => {
    fileStatus.value = '文件读取失败'
  }
  reader.readAsDataURL(file)
}

async function copyFileBase64() {
  if (!fileBase64.value) return
  try {
    await navigator.clipboard.writeText(fileBase64.value)
    fileStatus.value = 'Base64 已复制到剪贴板'
  } catch {
    fileStatus.value = '复制失败，请手动复制'
  }
}

function clearFileSide() {
  fileStatus.value = '等待上传图片'
  fileBase64.value = ''
  fileInfo.value = null
  filePreview.value = ''
}

// Base64 -> 图片
const b64Input = ref<string>('')
const b64Status = ref<string>('输入或粘贴 Base64 数据')
const b64Preview = ref<string>('')
const b64Info = ref<ImageInfo | null>(null)
let parseTimer: number | undefined

function normalizeBase64(input: string) {
  const raw = input.trim()
  if (!raw) return { dataUrl: '', mime: '', b64: '' }
  if (raw.startsWith('data:')) {
    const comma = raw.indexOf(',')
    const header = raw.substring(0, comma)
    const match = header.match(/data:(.*?);base64/i)
    const mime = match?.[1] || 'image/png'
    const b64 = raw.substring(comma + 1).replace(/\s+/g, '')
    return { dataUrl: `data:${mime};base64,${b64}`, mime, b64 }
  }
  // 纯 base64，默认按 png 处理
  const b64 = raw.replace(/\s+/g, '')
  return { dataUrl: `data:image/png;base64,${b64}`, mime: 'image/png', b64 }
}

function base64ByteLength(b64: string) {
  // 去除非 base64 字符
  const cleaned = b64.replace(/[^A-Za-z0-9+/=]/g, '')
  const padding = cleaned.endsWith('==') ? 2 : cleaned.endsWith('=') ? 1 : 0
  return Math.max(0, Math.floor(cleaned.length * 3 / 4) - padding)
}

function convertFromBase64() {
  try {
    const { dataUrl, b64 } = normalizeBase64(b64Input.value)
    if (!dataUrl) {
      b64Preview.value = ''
      b64Info.value = null
      b64Status.value = '请输入 Base64 数据'
      return
    }
    // 快速校验 base64 可解码
    try { void atob(b64) } catch { throw new Error('Base64 数据格式错误') }
    b64Preview.value = dataUrl
    const img = new Image()
    img.onload = () => {
      b64Info.value = {
        width: img.width,
        height: img.height,
        sizeKB: Number(formatKB(base64ByteLength(b64))),
      }
      b64Status.value = '解析完成'
    }
    img.onerror = () => {
      b64Status.value = '图片加载失败，可能是无效的 Base64 数据'
    }
    img.src = dataUrl
  } catch (e: any) {
    b64Status.value = '解析失败：' + (e?.message || '未知错误')
    b64Preview.value = ''
    b64Info.value = null
  }
}

watch(b64Input, () => {
  if (parseTimer) clearTimeout(parseTimer)
  // 轻量防抖，提升输入体验
  parseTimer = window.setTimeout(convertFromBase64, 400)
})

async function copyParsedDataUrl() {
  if (!b64Preview.value) return
  try {
    await navigator.clipboard.writeText(b64Preview.value)
    b64Status.value = 'Data URL 已复制到剪贴板'
  } catch {
    b64Status.value = '复制失败，请手动复制'
  }
}

function clearB64Side() {
  b64Input.value = ''
  b64Preview.value = ''
  b64Info.value = null
  b64Status.value = '输入或粘贴 Base64 数据'
}
</script>

<template>
  <section class="space-y-6">
    <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
      <!-- 图片 -> Base64 -->
      <div class="p-4 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
        <h3 class="text-base font-semibold mb-3 flex items-center gap-2">
          <i class="fa fa-image text-primary"></i>
          图片转 Base64
        </h3>
        <div class="space-y-3">
          <input type="file" accept="image/*" @change="onFileChange" />
          <p class="text-sm text-gray-500 dark:text-gray-400">{{ fileStatus }}</p>
          <div v-if="filePreview" class="mt-2">
            <img :src="filePreview" alt="预览" class="max-h-48 rounded border border-gray-200 dark:border-gray-700" />
          </div>
          <div v-if="fileInfo" class="grid grid-cols-3 gap-2">
            <div class="bg-gray-50 dark:bg-gray-700 p-2 rounded"><p class="text-xs text-gray-500">宽度</p><p class="font-semibold">{{ fileInfo.width }} px</p></div>
            <div class="bg-gray-50 dark:bg-gray-700 p-2 rounded"><p class="text-xs text-gray-500">高度</p><p class="font-semibold">{{ fileInfo.height }} px</p></div>
            <div class="bg-gray-50 dark:bg-gray-700 p-2 rounded"><p class="text-xs text-gray-500">大小</p><p class="font-semibold">{{ fileInfo.sizeKB }} KB</p></div>
          </div>
          <div v-if="fileBase64" class="space-y-2">
            <label class="block text-sm font-medium">Base64（含 data URI）</label>
            <textarea readonly class="w-full h-40 p-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 font-mono text-xs resize-y">{{ fileBase64 }}</textarea>
            <div class="flex gap-2">
              <button class="px-3 py-1 bg-primary text-white rounded hover:bg-primary/90" @click="copyFileBase64">复制</button>
              <button class="px-3 py-1 bg-gray-200 dark:bg-gray-700 rounded hover:bg-gray-300 dark:hover:bg-gray-600" @click="clearFileSide">清空</button>
            </div>
          </div>
        </div>
      </div>

      <!-- Base64 -> 图片 -->
      <div class="p-4 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
        <h3 class="text-base font-semibold mb-3 flex items-center gap-2">
          <i class="fa fa-code text-primary"></i>
          Base64 转 图片
        </h3>
        <div class="space-y-3">
          <label class="block text-sm font-medium">Base64 数据（可直接粘贴 data:image/...;base64, 前缀）</label>
          <textarea v-model="b64Input" class="w-full h-40 p-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 font-mono text-xs resize-y" placeholder="例如：... 或纯 Base64 字符串"></textarea>
          <p class="text-sm text-gray-500 dark:text-gray-400">{{ b64Status }}</p>
          <div v-if="b64Preview" class="mt-2">
            <img :src="b64Preview" alt="预览" class="max-h-48 rounded border border-gray-200 dark:border-gray-700" />
          </div>
          <div v-if="b64Info" class="grid grid-cols-3 gap-2">
            <div class="bg-gray-50 dark:bg-gray-700 p-2 rounded"><p class="text-xs text-gray-500">宽度</p><p class="font-semibold">{{ b64Info.width }} px</p></div>
            <div class="bg-gray-50 dark:bg-gray-700 p-2 rounded"><p class="text-xs text-gray-500">高度</p><p class="font-semibold">{{ b64Info.height }} px</p></div>
            <div class="bg-gray-50 dark:bg-gray-700 p-2 rounded"><p class="text-xs text-gray-500">大小</p><p class="font-semibold">{{ b64Info.sizeKB }} KB</p></div>
          </div>
          <div v-if="b64Preview" class="flex gap-2">
            <button class="px-3 py-1 bg-primary text-white rounded hover:bg-primary/90" @click="copyParsedDataUrl">复制 Data URL</button>
            <button class="px-3 py-1 bg-gray-200 dark:bg-gray-700 rounded hover:bg-gray-300 dark:hover:bg-gray-600" @click="clearB64Side">清空</button>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

<style scoped></style>